import os
import subprocess
from typing import List, Set, Union

import sys
from typing import Tuple, Generator
from ..base import BaseWebSocketHandler
from ...utils import remove_gitignore, create_background_task
from ...app.configs import AppConfig

CFG_ITEM_FOLDERS_TO_INDEX = "git_repositories_indexing/search_paths"
AppConfig().register_config_item(CFG_ITEM_FOLDERS_TO_INDEX, [])


def get_dirs_and_files(path) -> Tuple[Set[str], Set[str]]:
    files, dirs = set(), set()
    for item in os.listdir(path):
        item_abspath = os.path.join(path, item)
        if os.path.isdir(item_abspath):
            dirs.add(item)
        else:
            files.add(item)
    return dirs, files


def find_git_repos(base_paths, ignored_paths) -> Generator[str, None, None]:
    for base_path in base_paths:
        if not os.path.exists(base_path):
            raise FileNotFoundError(f"Path {base_path} does not exist")
    for base_path in base_paths:
        yield from search_git_repo_in_directory(base_path, ignored_paths)


def search_git_repo_in_directory(
    base_path, ignored_paths
) -> Generator[str, None, None]:
    """
    Search for git repositories in a single path
    :param base_path: path to search in
    :param ignored_paths: paths to ignore
    :return: generator of git repositories paths
    """
    if base_path in ignored_paths:
        yield from []

    dirs, files = get_dirs_and_files(base_path)
    if ".git" in dirs:
        yield base_path
        dirs.remove(".git")
    if "node_modules" in dirs:
        dirs.remove("node_modules")
    if ".gitignore" in files:
        gitignore_path = os.path.join(base_path, ".gitignore")
        dirs = remove_gitignore(gitignore_path, dirs)
    for i, dir in enumerate(dirs):
        if i < 100:  # 对子文件夹数量大于100的文件夹，只搜索前100个，避免循环次数过多
            yield from search_git_repo_in_directory(
                os.path.join(base_path, dir), ignored_paths
            )


def get_remote_urls(repo_path):
    """获取git仓库的所有远程地址"""
    remote_branches = []
    command = ["git", "-C", repo_path, "remote", "-v"]
    result = subprocess.run(command, capture_output=True, text=True)
    if result.returncode == 0:
        for line in result.stdout.strip().split("\n"):
            parts = line.split()
            if len(parts) == 3 and parts[2] == "(fetch)":
                remote_branches.append((parts[0], parts[1]))
    return remote_branches


def config_git_repo_searching_paths(paths: List[str]) -> str:
    AppConfig().update_config_items(CFG_ITEM_FOLDERS_TO_INDEX, paths)
    return "update config item successfully"


class GitReposIndexingWSHandler(BaseWebSocketHandler):
    def on_open(self):
        base_paths = AppConfig().get_config_item(CFG_ITEM_FOLDERS_TO_INDEX)
        ignored_paths = [
            r"C:\Users\houzh\Developing\yy\2022-08-11-juliet-c-cplusplus-v1-3-1-with-extra-support"
        ]
        self.search_iterator = find_git_repos(base_paths, ignored_paths)
        # TODO: 增加记忆功能，断开重连也能update
        self.task = create_background_task(
            "search_docs",
            self.search_loop,
            (),
            on_finish=self.handle_finish,
        )
        print("task_data", self.task.task_data)

        if not self.task.task_data.get("git_repos"):
            self.task.run()
            self.search_result = []
        else:
            self.search_result = self.task.task_data.get("git_repos")
            self.ctx.send_message(
                {"type": "initial", "data": self.search_result}
            )
            print("sent initial data", len(self.search_result))

    def search_loop(self):
        print("Starting indexing loop")
        remotes_count = 0
        for i, repo_path in enumerate(self.search_iterator):
            remote_branches = get_remote_urls(repo_path)
            repo_data = {"id": i + 1, "local_path": repo_path, "remote_branches": []}

            for remote, url in remote_branches:
                repo_data["remote_branches"].append(
                    {"id": remotes_count, "remote": remote, "url": url}
                )
                remotes_count += 1
            # 如果远程分支数量不为0，就让根节点默认展示第一个远程分支
            if len(remote_branches) > 0:
                repo_data["url"] = repo_data["remote_branches"][0]["url"]
                repo_data["remote"] = repo_data["remote_branches"][0]["remote"]
            self.search_result.append(repo_data)
            self.ctx.send_message({"type": "update", "data": [repo_data]})
            if i > 10:
                break
        self.ctx.send_message({"type": "status", "data": {"status": "finish"}})
        self.task.task_data["git_repos"] = self.search_result

    def handle_finish(self):
        pass


def cli_main():
    base_path = r"C:\Users\houzh\Developing"
    ignored_paths = [
        r"C:\Users\houzh\Developing\yy\2022-08-11-juliet-c-cplusplus-v1-3-1-with-extra-support"
    ]
    # git_repositories =

    for i, repo_path in enumerate(find_git_repos(base_path, ignored_paths)):
        print(f"Git仓库#{i+1}: {os.path.basename(repo_path)}")
        print(f"本地路径: {repo_path}")
        remote_branches = get_remote_urls(repo_path)
        for remote, url in remote_branches:
            print(f"  远程地址: {remote} - {url}")
        print()


if __name__ == "__main__":
    cli_main()
